package com.bai.localshop.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.aliyun.oss.ServiceException;
import com.bai.localshop.context.BaseContext;
import com.bai.localshop.exception.BaseException;
import com.bai.localshop.mapper.ProductCategoryRelationMapper;
import com.bai.localshop.pojo.dto.ProductUpdateDTO;
import com.bai.localshop.pojo.entity.ProductCategoryRelation;
import com.bai.localshop.service.AuthService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bai.localshop.mapper.ProductMapper;
import com.bai.localshop.pojo.dto.ProductQueryDTO;
import com.bai.localshop.pojo.dto.ProductSaveDTO;
import com.bai.localshop.pojo.entity.Product;
import com.bai.localshop.pojo.vo.ProductVO;
import com.bai.localshop.service.ProductService;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.bai.localshop.constant.RedisKeyConstant.*;
import static com.bai.localshop.constant.SystemConstants.ROLE_ADMIN;

/**
 * 商品服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    private final ProductMapper productMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final RBloomFilter<String> createCachePenetrationBloomFilter;
    private final RedissonClient redissonClient;
    private final AuthService userService;
    private final ProductCategoryRelationMapper productCategoryRelationMapper;

    /**
     * 分页查询商品
     */
    @Override
    public <T> IPage<T> pageQuery(ProductQueryDTO requestParam, Class<T> voClass) {
        //获取用户身份
        String roleName = userService.getUserById(BaseContext.getCurrentId()).getRoleName();
        //身份是普通用户
        //todo 抽取常量类
        if (!"管理员".equals(roleName)) {
            // 用户端固定条件：只查询上架商品
            requestParam.setStatus(1);
            // 管理员专用条件设置为空
            requestParam.setMinPrice(null);
            requestParam.setMaxPrice(null);
        }
        // 设置默认分页参数
        int page = requestParam.getPage() != null ? requestParam.getPage() : 1;
        int pageSize = requestParam.getPageSize() != null ? requestParam.getPageSize() : 10;

        // 执行分页查询
        IPage<Product> productIPage = baseMapper.selectProductPage(
                new Page<>(page, pageSize),
                requestParam
        );
        // 动态转换为不同的VO
        return productIPage.convert(each -> BeanUtil.toBean(each, voClass));
    }

    /**
     * 根据id查询商品
     */
    @Override
    public ProductVO getById(Long id) {
        //查询缓存
        String productStr = stringRedisTemplate.opsForValue().get(String.format(PRODUCT_KEY, id));
        if (StrUtil.isNotBlank(productStr)) {
            return JSON.parseObject(productStr, ProductVO.class);
        }
        //检查空值缓存
        if (StrUtil.isNotBlank(stringRedisTemplate.opsForValue().get(String.format(NULL_PRODUCT_KEY, id)))) {
            return null;
        }
        //检查布隆过滤器中是否有数据（防止缓存穿透）
        boolean contains = createCachePenetrationBloomFilter.contains(id.toString());
        if (!contains) {
            return null;
        }
        //获取分布式锁对象
        RLock lock = redissonClient.getLock("lock_product:" + id);
        //获取分布式锁

        try {
            boolean locked = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (!locked) {
                throw new RuntimeException("获取锁失败");
            }
            //再次检查缓存
            productStr = stringRedisTemplate.opsForValue().get(String.format(PRODUCT_KEY, id));
            if (StrUtil.isNotBlank(productStr)) {
                return JSON.parseObject(productStr, ProductVO.class);
            }
            //查询数据库
            Product product = baseMapper.selectById(id);
            if (product == null) {
                //数据库中确实无数据，缓存空值
                stringRedisTemplate.opsForValue().set(String.format(NULL_PRODUCT_KEY, id),
                        "-",
                        30,
                        TimeUnit.MINUTES);
                return null;
            }
            //不为空，缓存重建
            ProductVO productVO = BeanUtil.copyProperties(product, ProductVO.class);
            stringRedisTemplate.opsForValue().set(
                    String.format(PRODUCT_KEY, id),
                    JSON.toJSONString(productVO),
                    2,
                    TimeUnit.HOURS);
            return productVO;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 查询热门商品数据
     */
    @Override
    public List<ProductVO> getHotProduct() {
        //查询缓存
        String cache = stringRedisTemplate.opsForValue().get(String.format(HOT_PRODUCT_LIST_KEY));
        if (StrUtil.isNotBlank(cache)) {
            return JSON.parseArray(cache, ProductVO.class);
        }
        //未命中
        LambdaQueryWrapper<Product> queryWrapper = Wrappers.lambdaQuery(Product.class)
                .eq(Product::getIsHome, true)
                .eq(Product::getStatus, 1)
                .orderByDesc(Product::getSort);
        List<Product> hotProducts = baseMapper.selectList(queryWrapper);
        List<ProductVO> result = BeanUtil.copyToList(hotProducts, ProductVO.class);
        //重建缓存
        stringRedisTemplate.opsForValue().set(
                String.format(HOT_PRODUCT_LIST_KEY),
                JSON.toJSONString(result),
                2,
                TimeUnit.HOURS
        );
        return result;
    }

    /**
     * 新增商品
     */
    @Override
    public void save(ProductSaveDTO requestParam) {
        //如果排序在50以上，显示在主页
        if (requestParam.getSort() >= 50) {
            requestParam.setIsHome(true);
        }
        Product product = BeanUtil.copyProperties(requestParam, Product.class);
        baseMapper.insert(product);
        //向商品分类联合表插入数据
        ProductCategoryRelation productCategoryRelation = new ProductCategoryRelation();
        productCategoryRelation.setProductId(product.getId());
        productCategoryRelation.setCategoryId(requestParam.getCategoryId());
        productCategoryRelationMapper.insert(productCategoryRelation);
        //缓存预热
        stringRedisTemplate.opsForValue().set(
                String.format(PRODUCT_KEY, product.getId()),
                JSON.toJSONString(BeanUtil.copyProperties(product, ProductVO.class)),
                2,
                TimeUnit.HOURS);
        //如果是热门商品数据,删除列表缓存
        if (product.getIsHome()) {
            stringRedisTemplate.delete(HOT_PRODUCT_LIST_KEY);
        }
        createCachePenetrationBloomFilter.add(product.getId().toString());
    }

    /**
     * 更新商品
     */
    @Override
    public void update(ProductUpdateDTO requestParam) {
        //查询商品对象
        LambdaQueryWrapper<Product> queryWrapper = Wrappers.lambdaQuery(Product.class)
                .eq(Product::getId, requestParam.getId());
        Product hasProduct = baseMapper.selectOne(queryWrapper);
        if (hasProduct == null) {
            throw new BaseException("商品不存在");
        }
        //获取分布式读写锁
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(String.format(PRODUCT_UPDATE_LOCK_KEY, requestParam.getId()));
        RLock rLock = readWriteLock.writeLock();
        if (!rLock.tryLock()) {
            throw new ServiceException("商品正在被更新，请稍后再试...");
        }
        try {
            LambdaUpdateWrapper<Product> updateWrapper = Wrappers.lambdaUpdate(Product.class)
                    .eq(Product::getId, requestParam.getId());
            Product product = Product.builder()
                    .name(requestParam.getName())
                    .description(requestParam.getDescription())
                    .price(requestParam.getPrice())
                    .image(requestParam.getImage())
                    .categoryId(requestParam.getCategoryId())
                    .stock(requestParam.getStock())
                    .status(requestParam.getStatus())
                    .detail(requestParam.getDetail())
                    .sort(requestParam.getSort())
                    .isHome(requestParam.getIsHome())
                    .updateTime(LocalDateTime.now())
                    .build();
            int updateCount = baseMapper.update(product, updateWrapper);

            if (updateCount == 0) {
                throw new BaseException("商品更新失败");
            }
            //更新商品分类联合表表中数据
            //删除原本分类
            LambdaQueryWrapper<ProductCategoryRelation> deleteWrapper = Wrappers.lambdaQuery(ProductCategoryRelation.class)
                    .eq(ProductCategoryRelation::getProductId, requestParam.getId());
            productCategoryRelationMapper.delete(deleteWrapper);
            //插入新的分类
            ProductCategoryRelation productCategoryRelation = new ProductCategoryRelation();
            productCategoryRelation.setCategoryId(requestParam.getCategoryId());
            productCategoryRelation.setProductId(requestParam.getId());
            productCategoryRelationMapper.insert(productCategoryRelation);
            if (!Objects.equals(product.getIsHome(), hasProduct.getIsHome())) {
                //删除热门缓存
                stringRedisTemplate.delete(HOT_PRODUCT_LIST_KEY);
            }
            // 删除旧缓存
            stringRedisTemplate.delete(String.format(PRODUCT_KEY, requestParam.getId()));
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 根据id删除商品
     */
    @Override
    public void deleteById(Long id) {
        //检查商品是否存在
        Product product = baseMapper.selectById(id);
        if (product == null) {
            throw new BaseException("商品不存在");
        }
        // 删除所有相关缓存
        Set<String> keysToDelete = new HashSet<>();
        keysToDelete.add(String.format(PRODUCT_KEY, id));
        keysToDelete.add(String.format(NULL_PRODUCT_KEY, id));
        keysToDelete.add(String.format(HOT_PRODUCT_LIST_KEY));
        stringRedisTemplate.delete(keysToDelete);
        //删除数据库数据
        productMapper.deleteById(id);
        //删除商品分类联合表表中数据
        LambdaQueryWrapper<ProductCategoryRelation> deleteWrapper = Wrappers.lambdaQuery(ProductCategoryRelation.class)
                .eq(ProductCategoryRelation::getProductId, id);
        productCategoryRelationMapper.delete(deleteWrapper);
    }

    /**
     * 修改商品状态
     */
    @Override
    public void updateStatus(Long id, Integer status) {
        // 校验状态值
        if (status != 0 && status != 1) {
            throw new BaseException("状态值不合法");
        }
        Product existProduct = baseMapper.selectById(id);
        if (existProduct == null) {
            throw new BaseException("商品不存在");
        }
        Product product = new Product();
        product.setId(id);
        product.setStatus(status);
        productMapper.updateById(product);
        // 清理所有相关缓存
        Set<String> keysToDelete = new HashSet<>();
        keysToDelete.add(String.format(PRODUCT_KEY, id));
        keysToDelete.add(String.format(NULL_PRODUCT_KEY, id));
        // 如果商品下架
        if (status == 0) {
            keysToDelete.add(HOT_PRODUCT_LIST_KEY);
        } else if (status == 1 && existProduct.getIsHome()) {
            // 从上架状态变为上架，且是热门商品，也要清理热门缓存
            keysToDelete.add(HOT_PRODUCT_LIST_KEY);
        }
        stringRedisTemplate.delete(keysToDelete);
    }
}


